package q.yazg;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.swing.*;
import q.yazg.AnimatedEntity.AnimationState;
import org.json.JSONException;
import static q.yazg.Utils.*;

/**
 *
 * @author khalisq
 */
public class PlayerEntity extends AnimatedEntity implements Followable{
        
    private static final float X_WALK_SPEED = Constants.getFloat("player.walkspeed", 0.1f)/1000,
                               X_RUN_SPEED  = Constants.getFloat("player.runspeed", 0.2f)/1000;
    private static final float JUMP_VEL     = (float) (Math.sqrt(2*Main.G*Constants.getFloat("player.jumpheight", 2)));
    private static final float X_WALK_ACCEL = X_WALK_SPEED / (Constants.getFloat("player.acceleration", 0.01f)*1000),
                               X_RUN_ACCEL  = X_RUN_SPEED  / (Constants.getFloat("player.acceleration", 0.01f)*1000);
    private static final float X_DAMP       = Constants.getFloat("player.damping", 0.05f);
    
    private static final JTextField DEBUG_VY = (JTextField) (Main.DEBUG_WINDOW_VISIBLE?Main.DEBUG_WINDOW.add(new JTextField()):null);
    static{
        DEBUG_VY.setEditable(false);
        Main.DEBUG_WINDOW.pack();
    }
    
    private static final InternalMessage MSG_STARTFALL = new InternalMessage("FALLING"),
                                         MSG_LAND      = new InternalMessage("LAND"),
                                         MSG_FORWARD   = new InternalMessage("FORWARD"),
                                         MSG_BACKWARD  = new InternalMessage("BACKWARD");
    
    private float vy = JUMP_VEL, vx = 0f;
    private float lx = 0f, ly = 0f;
    
    private boolean onGround = false;
    
    static State beforeSuperM3gaL337J0lvlP = State.idle;
    
    public PlayerEntity() throws JSONException, FileNotFoundException, IOException{
        super(State.idle);
        parse(new File("anim.json"));
        Main.msgThread.addListener(MouseMessage.class, this);
        Main.msgThread.sendMessage(FollowMessage.setInstance(this));
    }

    @Override
    public float getDirection() {
        return direction;
    }

    @Override
    public float getVelocity() {
        switch((State)currentState){
            case idle: return 0.0f;
            case moving: return X_WALK_SPEED;
            case running: return X_RUN_SPEED;
            default: return vx;
        }
    }
    
    enum State implements StateProvider, AnimationState{

        idle{
            
            public AnimationState message(Message msg){
                AnimationState as = super.message(msg);
                if(as!=null)return as;
                if(msg==MSG_FORWARD)return moving;
                return idle;
            }
            
        },
        moving{
            
            public AnimationState message(Message msg){
                AnimationState as = super.message(msg);
                if(as!=null)return as;
                if(msg==MSG_FORWARD)return running;
                if(msg==MSG_BACKWARD)return idle;
                return this;
            }
            
        },
        running{
            
            public AnimationState message(Message msg){                
                AnimationState as = super.message(msg);
                if(as!=null)return as;
                if(msg==MSG_BACKWARD)return moving;
                return this;
            }
            
        },
        jumping(){
            
            public AnimationState message(Message msg){
                if(msg==MSG_STARTFALL) return falling;
                return this;
            }
            
        },
        falling(){
            
            public AnimationState message(Message msg){
                if(msg==MSG_LAND) return beforeSuperM3gaL337J0lvlP;
                return this;
            }
            
        };

        @Override
        public AnimationState getState(String name) {
            return valueOf(name);
        }
        
        @Override
        public AnimationState getDefaultState(){
            return idle;
        }

        @Override
        public AnimationState message(Message msg) {
            Main.msgThread.sendMessage(FollowMessage.updateInstance(null));
            if(msg == MouseMessage.moveUp) return jumping;
            return null;
        }
        
    }

    public void update(long ticks){
        super.update(ticks);
        if(Main.DEBUG_WINDOW_VISIBLE){
            DEBUG_VY.setText(String.valueOf(vy));
        }
        switch((State)currentState){
            case moving:
                if(abs(vx)<X_WALK_SPEED){
                    vx+=direction*X_WALK_ACCEL*ticks;
                    vx=abs(vx)>X_WALK_SPEED?direction*X_WALK_SPEED:vx;
                }
                break;
            case running:
                if(abs(vx)<X_RUN_SPEED){
                    vx+=direction*X_RUN_ACCEL*ticks;
                    vx=abs(vx)>X_RUN_SPEED?direction*X_RUN_SPEED:vx;
                }
                break;
            case idle:
                vx*=Math.pow(X_DAMP, ticks);
        }
        lx=x; ly=y;
        x+=vx*ticks;
        x=Main.collideWithTerrainX(this, lx);
        onGround=Main.checkOnGround(this);
        y+=vy*ticks;
        if(onGround){
            if(currentState!=State.jumping)vy=0;
            if(currentState==State.falling)message(MSG_LAND);
        } else {
            y=Main.collideWithTerrainY(this);
            vy-=Main.G * ticks;
        }
        if(vy!=JUMP_VEL){
            if(currentState==State.jumping && vy<-0.0f)message(MSG_STARTFALL);
        }
    }
    
    public void message(Message msg){
        //System.err.println("Message received: "+msg);
        if(msg==MouseMessage.moveLeft && direction==DIRECTION_RIGHT && currentState==State.idle){
            Main.msgThread.sendMessage(FollowMessage.updateInstance(this));
            direction*=-1;
            return;
        }
        if(msg==MouseMessage.moveRight && direction==DIRECTION_LEFT && currentState==State.idle){
            Main.msgThread.sendMessage(FollowMessage.updateInstance(this));
            direction*=-1;
            return;
        }
        if(msg==MouseMessage.moveRight){
            super.message(direction==DIRECTION_RIGHT?MSG_FORWARD:MSG_BACKWARD);
            return;
        }
        if(msg==MouseMessage.moveLeft){
            super.message(direction==DIRECTION_LEFT?MSG_FORWARD:MSG_BACKWARD);
            return;
        }
        if(msg==MouseMessage.moveUp && onGround){
            beforeSuperM3gaL337J0lvlP = (State) currentState;
            vy=JUMP_VEL;
            onGround=false;
        }
        super.message(msg);
        
    }
    
    public void setOnGround(){
        System.err.println("setOnGround()");
        vy=0;
        onGround=true;
        if(currentState==State.falling)message(MSG_LAND);
    }
    
    public void gowbeck(){
        x=lx; y=ly;
        forceState(State.idle);
    }
}